//#ifndef _CG_FILE_H
//#define _CG_FILE_H

#include <xmmintrin.h>
#include <math.h>
#include <iostream>
#include <fstream>
#ifdef WIN32
# include <windows.h>
#endif
#ifdef __APPLE__
#include <OpenGL/gl.h>
#include <OpenGL/glext.h>
#include <GLUT/glut.h>
#else
# include <GL/gl.h>
# include <GL/glut.h>
# include <GL/glext.h>
#endif
#include <Cg\cg.h>
#include <Cg\cgGL.h>

#ifndef CWD
# define CWD ""
#endif

// texture 
typedef struct TEXTURE2D
{
	GLuint id;		// OpenGL texture id
	GLubyte* data;
	int width;
	int height;
	int maxcol;		// max. colors
} Texture2D;

static Texture2D g_Texture;		// texture structure

static CGcontext Context = NULL;
static CGprogram fragmentProgram = NULL;
static CGprogram vertexProgram = NULL;

//----------------------------------------------------
// Non-uniform parameters 
//----------------------------------------------------

static CGparameter testTextureParam = NULL;
static CGparameter decalCoordsParam = NULL;

static CGprofile fragmentprofile;
static CGprofile vertexprofile;

// Number of points in the image
static int xsize = 30;
static int ysize = 30;

static GLfloat *uv;

static bool load = true; 
static bool image = true;

static const float PI = 3.1415926;

static Texture2D new_Texture;	


static unsigned __int64 rdtsc( void) {
   __asm rdtsc;
}

void CheckCgError(void);
// -----------------------------------------------
// Load texture from given ppm file
// 
// @param   _filename    - path to file (PPM format)
// @return  true if successful, false otherwise
// With Thanks to Ram Nair for this function
// -----------------------------------------------
static bool load_ppm(char* _filename)
{
    FILE* stream;
    char line[100];

    /* can file be accessed ? */
    if ( (stream = fopen(_filename,"rb")) == NULL )
    {
        printf("ERROR: unable to access file !");
        return false;
    }

    /* file header */
    if ( fscanf( stream,
                "%s ", line ) != 1 || strcmp(line,"P6")!=0 )
    {
        printf("ERROR: file header invalid (must be \'P6\')");
        return false;
    }

	/* texture width */
	if ( fscanf( stream,
				"%d ", &g_Texture.width ) != 1 )
    {
        printf("ERROR: width value invalid !");
        return false;
    }

	/* texture height*/
	if ( fscanf( stream,
				"%d ", &g_Texture.height ) != 1 )
    {
        printf("ERROR: height value invalid !");
        return false;
    }

	/* max. color value */
	if ( fscanf( stream,
				"%d ", &g_Texture.maxcol ) != 1 || g_Texture.maxcol>65535)
    {
        printf("ERROR: max. colors value invalid (must be between 0 and 65535)");
        return false;
    }

    /* read from file */

	// no. of values to read = RGB x texture area
	size_t valuesToRead = g_Texture.width * g_Texture.width * 3;

    // 1 byte per color
	if (g_Texture.maxcol<=255)
	{
        GLubyte* data = (GLubyte*)malloc(sizeof(GLubyte) * valuesToRead);

        // read from file
        if ( fread(data, sizeof(GLubyte),
            valuesToRead, stream) < valuesToRead )
        {
            printf("ERROR: RGB color data invalid");
            return false;
        }

		// build our texture
		gluBuild2DMipmaps(GL_TEXTURE_2D, 3, g_Texture.width, g_Texture.height, GL_RGB, GL_UNSIGNED_BYTE, data);

		// free mem
		free(data);
	}
    // 2 bytes per color
	else
	{
        GLushort* data = (GLushort*)malloc(sizeof(GLushort) * valuesToRead);

        // read from file
        if ( fread(data, sizeof(GLushort),
            valuesToRead, stream) < valuesToRead )
        {
            printf("ERROR: RGB color data invalid");
            return false;
        }
		// build our texture
		gluBuild2DMipmaps(GL_TEXTURE_2D, 3, g_Texture.width, g_Texture.height, GL_RGB, GL_UNSIGNED_BYTE, data);
		// free mem
		free(data);
	}

	// close file
	fclose(stream);

    return true;
}

// -----------------------------------------------
// Load texture from given 24-bit bitmap file
//
// @param   _filename    - path to file (BMP format)
// @return  true if successful, false otherwise
// With Thanks to Ram Nair for this function
// -----------------------------------------------
static bool load_bmp24(char* _filename)
{
    FILE* stream;
    unsigned short int fileType;
    short int numPlanes, bitsPerPixel;
    long int dataOffset;

    /* can file be accessed ? */
    if ( (stream = fopen(_filename,"rb")) == NULL )
    {
        printf("ERROR: unable to access file !");
        return false;
    }
    /* check that file is a bitmap */
    if(fread(&fileType, sizeof(short int), 1, stream) < 1 || fileType!=19778)
    {
        printf("ERROR: invalid bitmap header!");
        return false;
    }
    /* skip file size and reserved fields of bitmap file header */
    fseek(stream, 8, SEEK_CUR);
    /* get the position of the actual bitmap data */
    if (fread(&dataOffset, sizeof(long int), 1, stream) < 1)
    {
        printf("ERROR: unable to get position of data bits !"); 
        return false;
    }
    /* skip size of bitmap info header */
    fseek(stream, 4, SEEK_CUR);
    /* get the width of the bitmap */
    if (fread(&g_Texture.width, sizeof(int), 1, stream) < 1)
    {
        printf("ERROR: unable to get width !");
        return false;
    }
    /* get the height of the bitmap */
    if ( fread(&g_Texture.height, sizeof(int), 1, stream) < 1 )
    {
        printf("ERROR: unable to get height !");
        return false;
    }

	/* get the number of planes (must be set to 1) */
    if ( fread(&numPlanes, sizeof(short int), 1, stream) < 1 || numPlanes != 1 )
    {
        printf("ERROR: number of planes must be 1 !");
        return false;
    }
    /* get the number of bits per pixel */
    if ( fread(&bitsPerPixel, sizeof(short int), 1, stream) < 1 || bitsPerPixel!=24 )
    {
        printf("ERROR: bits per pixel must be 24 !"); 
        return false;
    }

    /* seek to the actual data */
    fseek(stream, dataOffset, SEEK_SET);

    /* setup mem for data */
    size_t valuesToRead = g_Texture.width * g_Texture.height * 3;
    GLubyte* data = (GLubyte*)malloc(sizeof(GLubyte) * valuesToRead);

    /* read from file */
    if ( fread(data, sizeof(GLubyte),
        valuesToRead, stream) < valuesToRead )
    {
        printf("ERROR: RGB color data invalid");
        return false;
    }

	g_Texture.data = data;

	// build our texture
    // NOTE: bmp color values are in BGR order
	gluBuild2DMipmaps(GL_TEXTURE_2D, 3, g_Texture.width, g_Texture.height, GL_BGR_EXT, GL_UNSIGNED_BYTE, data);

	// free mem
	//free(data);

	// close file
	fclose(stream);

    return true;
}

// -----------------------------------------------
// Get extension part of given filename
//
// @param   _filename
// @return  if successful, extension in lowercase (e.g. bmp) otherwise empty string
// With Thanks to Ram Nair for this function
// -----------------------------------------------
static char* get_file_extension(char* _filename)
{
    char* ret;          // to hold return val
    int numChars = 0;       // length of string
    int lastIndex = 0;  // last index of '.'
    int i;

    // get length of string
    char* ch = _filename;
    while (*ch!=0)
    {
        if (*ch=='.')
            lastIndex = numChars;

        ch++;
        numChars++;
    }

    // long enough ?
    if (numChars>3)
    {
        ret = new char[numChars];
        i = 0;
        ch = _filename+lastIndex+1;
        while (*ch!=0)
        {
            // make everything lowercase
            if ((int)*ch < 97)
                ret[i] = *ch + 32;      // get lowercase equivalent
            else
                ret[i] = *ch;

            i++;
            ch++;
        }
        ret[i] = 0;     // null-character to terminate string
    }


    return ret;
}

static bool load_texture(char* _filename)
{
    char* ext;      // file extension
	bool wrap = true;		// wrap texture

    printf("Loading texture...\t");

	/* set up texture */
	// allocate a texture name
    glGenTextures( 1, &g_Texture.id );
    // select our current texture
    glBindTexture( GL_TEXTURE_2D, g_Texture.id );
	// byte alignment
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    /* file format */

    ext = get_file_extension(_filename);

    // bitmap
    if (strcmp(ext,"bmp")==0)
    {
        if (!load_bmp24(_filename))
            return false;
    }
    // ppm
    else if(strcmp(ext,"ppm")==0)
    {
        if (!load_ppm(_filename))
            return false;
    }
    // unknown
    else
    {
        printf("ERROR: unknown file format");
        return false;
    }

	glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

	// wrap texture and setup bilinear filters
    //glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    //glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    //glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    //glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	
	// mix texture with color
  	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

    printf("done (", g_Texture.width, "x", g_Texture.height, " pixels)");
    return true;
}

// Functions:
void fir_filter2(float* RGB_org, float* RGB_new);

//---------------------------------------------------
// Main Function
//---------------------------------------------------
static void primary_ccr(void)
{
		
  size_t valuesToRead = g_Texture.width * g_Texture.height * 3;
  new_Texture.data = (GLubyte*)malloc(sizeof(GLubyte) * valuesToRead);


  // set up texture 
  // allocate a texture name
  glGenTextures( 1, &new_Texture.id );
  // select our current texture
  glBindTexture( GL_TEXTURE_2D, new_Texture.id);
  // byte alignment
  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

	unsigned __int64 start, time;
	time = 0;

	printf("Updating Texture....");
    
	// Set R,G and B arrays for parallelism
	//float* RGBf;

//	__m128* R;
//	__m128* G;
//	__m128* B;

	float* data_in = (float*)malloc(sizeof(float)*512*512*3);
    float* data_out = (float*) malloc(sizeof(float)*512*512*3);

	for (int i=0; i<512*512*3; i++)
		data_in[i] = float(g_Texture.data[i]);

	start = rdtsc();

		// Processing...
	fir_filter2(data_in, data_out);

	time = time + (rdtsc() - start);

	for (int i=0; i<512*512*3; i++)
        new_Texture.data[i] = GLubyte(data_out[i]);

  time = time/(512*512);

 // printf( "average cycles taken = %u\n",time);

  gluBuild2DMipmaps(GL_TEXTURE_2D, 3, g_Texture.width, g_Texture.height, GL_BGR_EXT, GL_UNSIGNED_BYTE, new_Texture.data);

  glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	
  // mix texture with color
  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

  cgGLSetTextureParameter(testTextureParam, new_Texture.id);
  cgGLEnableTextureParameter(testTextureParam);

//  free(new_Texture.data);
}

//#endif